package cn.it.shop.lucene;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TopDocs;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Service;
import org.wltea.analyzer.lucene.IKQueryParser;

import cn.it.shop.pojo.Goods;

/*
 * 把数据添加到索引库中, 从索引库中查询数据
 * */
@Service("luceneService")
@Aspect // 此类就是一个切面类
public class LuceneServiceImpl implements LuceneService {
	
	@Pointcut("execution(* cn.it.shop.service.impl.GoodsServiceImpl.queryByName(..))")
	private void demo01(){}
	
	@Resource
	private LuceneUtil luceneUtil=null;
	
	@Resource
	private HighlighterUtil highlighterUtil=null;
	
	@Resource
	private DocumentUtil documentUtil=null;
	
	@Resource
	private Configuraction configuraction=null;

	/* (non-Javadoc)
	 * @see cn.it.shop.lucene.LuceneService#queryGoods(java.lang.String)
	 */
	
	// 环绕通知参数必须为: ProceedingJoinPoint 此类型是joinPoint子类型
	@Around(value="demo01()")
	public List<Goods> queryGoodsProxy(ProceedingJoinPoint joinPoint){
		Object object=null;
		try {
			System.out.println("----queryGoodsProxy----");
			// 执行了goodsService.queryByName();
			object=joinPoint.proceed();
			System.out.println("object:" + object);
			// 实现真正的查询功能
			object=this.queryGoods(joinPoint.getArgs()[0].toString());
		} catch (Throwable e) {
			throw new RuntimeException(e);
		}
		return (ArrayList<Goods>)object;
	}
	
	@Override
	public List<Goods> queryGoods(String keyWord) {
		List<Goods> goodsList = new ArrayList<Goods>();
		IndexSearcher indexSearcher = null;
		try {
			indexSearcher = luceneUtil.getIndexSearcher();
			// 可以按照名称,简单介绍,类别查询商品信息
			System.out.println("keyWord:" + keyWord);
			Query query=IKQueryParser.parseMultiField(new String[]{"name","remark","type"},keyWord);
			Sort sort = new Sort(new SortField("id", SortField.INT, false),
					new SortField("price", SortField.DOUBLE, true));
			TopDocs topDocs = indexSearcher.search(query, null, 10, sort);
			System.out.println("真正查询到的记录数: " + topDocs.totalHits);
			ScoreDoc[] scoreDocs = topDocs.scoreDocs;
			for (int i = 0; i < scoreDocs.length; i++) {
				ScoreDoc scoreDoc = scoreDocs[i];
				Document doc = indexSearcher.doc(scoreDoc.doc);
				/************************ 实现对字段进行高亮的效果 ********************************/
				// result是高亮后的结果
				String result=highlighterUtil.setHighlighterText(query, doc.get("name"), 20);
				// 重新赋值给doc的某个字段
				doc.getField("name").setValue(result);
				result=highlighterUtil.setHighlighterText(query, doc.get("remark"),50);
				doc.getField("remark").setValue(result);
				// 给类别高亮
				//doc.getField("type").setValue(highlighterUtil.setHighlighterText(query, doc.get("type"),100));
				/******************************************************************************/
				// 把doc转化goods
				Goods goods = documentUtil.documentToGoods(doc);
				goodsList.add(goods);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return goodsList;
	}
	// 此方法切入的通知, 如果方法设置为控制,则不支持多态
	@AfterReturning(pointcut="execution(* cn.it.shop.service.impl.GoodsServiceImpl.save(..))")
	public void addGoodsProxy(JoinPoint joinPoint){
		Goods goods=(Goods)joinPoint.getArgs()[0];
		System.out.println("连接点所属的目标对象:" + joinPoint.getTarget());
		System.out.println("连接点方法原型:"+ joinPoint.getSignature());
		System.out.println("连接点的参数:" + goods + "id:" + goods.getId());
		this.addGoods((Goods)joinPoint.getArgs()[0]);
	}

	/* (non-Javadoc)
	 * @see cn.it.shop.lucene.LuceneService#addGoods(cn.it.shop.pojo.Goods)
	 */
	@Override // 如果配置的是通知,参数必须是JoinPoint(连接点)
	public void addGoods(Goods goods) {
		IndexWriter indexWriter = null;
		try {
			indexWriter = luceneUtil.getIndexWriter();
			// 把Goods转化为document对象,然后入库
			Document doc = documentUtil.goodsToDocument(goods);
			indexWriter.addDocument(doc);
			indexWriter.commit();
		} catch (Exception e) {
			// 回滚数据
			try {
				indexWriter.rollback();
				throw new RuntimeException(e);
			} catch (IOException e1) {
				throw new RuntimeException(e);
			}
		}
	}

	// 更新和删除包括查询,都是先操作term,通过term去找到相应doc,然后在执行操作
	/* (non-Javadoc)
	 * @see cn.it.shop.lucene.LuceneService#deleteGoods(int)
	 */
	@Override
	public void deleteGoods(int id) {
		IndexWriter indexWriter = null;
		try {
			indexWriter = new IndexWriter(configuraction.getDirectory(),
					configuraction.getAnalyzer(), MaxFieldLength.LIMITED);
			// 根据term去删除符合条件的doc(如果多个符合则会删除多个doc)
			indexWriter.deleteDocuments(new Term("id", id + ""));
			// 在删除doc的同时要同步更新term数据
			indexWriter.optimize(); // 同步term数据
			indexWriter.commit();
		} catch (Exception e) {
			// 回滚数据
			try {
				indexWriter.rollback();
				throw new RuntimeException(e);
			} catch (IOException e1) {
				throw new RuntimeException(e);
			}
		} finally {
			try {
				indexWriter.close();
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
	}

	/* (non-Javadoc)
	 * @see cn.it.shop.lucene.LuceneService#updateGoods(cn.it.shop.pojo.Goods)
	 */
	@Override
	public void updateGoods(Goods goods) {
		IndexWriter indexWriter = null;
		try {
			indexWriter = new IndexWriter(configuraction.getDirectory(),
					configuraction.getAnalyzer(), MaxFieldLength.LIMITED);
			// 索引库的更新是: 为了效率是先删除,在添加 ,首先根据Term 的条件删除相应的doc,然后在把新的DOC添加到库中
			indexWriter.updateDocument(
					new Term("id", goods.getId().toString()),
					documentUtil.goodsToDocument(goods));
			// 在更新doc的同时要同步更新term数据
			indexWriter.optimize(); // 同步term数据
			indexWriter.commit();
		} catch (Exception e) {
			// 回滚数据
			try {
				indexWriter.rollback();
				throw new RuntimeException(e);
			} catch (IOException e1) {
				throw new RuntimeException(e);
			}
		} finally {
			try {
				indexWriter.close();
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
	}
}
